﻿using SharpDepend.Classes;
using SharpDepend.Datatypes;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SharpDepend.WP8.Renderer
{
    /// <summary>
    /// Interaction logic for SoftwareRenderer.
    /// </summary>
    public class SoftwareRenderer : UserControl, IGraphicsDevice
    {
        private static int sID;

        private WriteableBitmap wBitmap;
        private BuiltInGraphicsHost mBuiltInGraphicsHost;
        private Image mImage;

        private int ID;

        public event Action ResolutionChanged;
        public event Action OnManualRender;

        /// <summary>
        /// Create instance of software renderer.
        /// </summary>
        public SoftwareRenderer()
        {
            ID = sID++;
            
            mBuiltInGraphicsHost = new BuiltInGraphicsHost(4, 4);
            mImage = new Image();
            Content = mImage;

            VerticalAlignment = VerticalAlignment.Stretch;
            VerticalContentAlignment = VerticalAlignment.Stretch;
            HorizontalAlignment = HorizontalAlignment.Stretch;
            HorizontalContentAlignment = HorizontalAlignment.Stretch;

            mImage.SizeChanged += SoftwareRenderer_SizeChanged;
            mImage.Loaded += SoftwareRenderer_Loaded;
            mImage.Stretch = System.Windows.Media.Stretch.Fill;
        }

        private bool IsAnotherSize(double width, double height)
        {
            return (int)width != mBuiltInGraphicsHost.RenderWidth || (int)height != mBuiltInGraphicsHost.RenderHeight;
        }

        void SoftwareRenderer_Loaded(object sender, RoutedEventArgs e)
        {
            int width = (int)this.DesiredSize.Width;
            int height = (int)this.DesiredSize.Height;
            width = Math.Max(4, width);
            height = Math.Max(4, height);

            SetViewportSize(
                Math.Max(RenderWidth, width),
                Math.Max(RenderHeight, height));
        }

        public void SetViewportSize(double width, double height)
        {
            if (IsAnotherSize(width, height))
            {
                width = Math.Max(4, (int)width);
                height = Math.Max(4, (int)height);

                wBitmap = new WriteableBitmap((int)width, (int)height);
                mBuiltInGraphicsHost.SetViewportSize((int)width, (int)height, wBitmap.Pixels);
                mImage.Source = wBitmap;

                var call = ResolutionChanged;
                if (call != null)
                {
                    call();
                }
            }
        }

        void SoftwareRenderer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            int width = (int)e.NewSize.Width;
            int height = (int)e.NewSize.Height;
            if (IsAnotherSize(width, height))
            {
                width = Math.Max(4, width);
                height = Math.Max(4, height);

                wBitmap = new WriteableBitmap(width, height);
                mBuiltInGraphicsHost.SetViewportSize(width, height, wBitmap.Pixels);
                mImage.Source = wBitmap;

                var call = ResolutionChanged;
                if (call != null)
                {
                    call();
                }
            }
        }

        public object Locker
        {
            get { return mBuiltInGraphicsHost.Locker; }
        }

        public int RenderWidth
        {
            get { return mBuiltInGraphicsHost.RenderWidth; }
        }

        public int RenderHeight
        {
            get { return mBuiltInGraphicsHost.RenderHeight; }
        }

        public bool Enable
        {
            get
            {
                return mBuiltInGraphicsHost.Enable;
            }
            set
            {
                mBuiltInGraphicsHost.Enable = value;
            }
        }

        public bool RenderTextures
        {
            get
            {
                return mBuiltInGraphicsHost.RenderTextures;
            }
            set
            {
                mBuiltInGraphicsHost.RenderTextures = value;
            }
        }

        public void RegisterTexture(Texture texture)
        {
            mBuiltInGraphicsHost.RegisterTexture(texture);
        }

        public void UnregisterTexture(Texture texture)
        {
            mBuiltInGraphicsHost.UnregisterTexture(texture);
        }

        public void UpdateTexture(Texture texture)
        {
            mBuiltInGraphicsHost.UpdateTexture(texture);
        }

        public void BeginRender()
        {
            mBuiltInGraphicsHost.BeginRender();
        }

        public void SetClip(Vector4 plane)
        {
            mBuiltInGraphicsHost.SetClip(plane);
        }

        public void ResetClip()
        {
            mBuiltInGraphicsHost.ResetClip();
        }

        public void Draw(ref Vector4 rectangle, SharpDepend.Datatypes.Color color)
        {
            mBuiltInGraphicsHost.Draw(ref rectangle, color);
        }

        public void Draw(ref Vector4 rectangle, SharpDepend.Datatypes.Color color, Texture texture, ref Vector4 destinationTextureUV)
        {
            mBuiltInGraphicsHost.Draw(ref rectangle, color, texture, ref destinationTextureUV);
        }

        public void DrawLine(Vector2 start, Vector2 end, SharpDepend.Datatypes.Color color)
        {
            mBuiltInGraphicsHost.DrawLine(start, end, color);
        }

        public void DrawCircle(Vector2 position, double radius, SharpDepend.Datatypes.Color color)
        {
            mBuiltInGraphicsHost.DrawCircle(position, radius, color);
        }

        public void EndRender()
        {
            // If the renderer is being rendering before the renderer element has initialized tha size.
            // It will be null. So null check it before.
            var bitmap = wBitmap;
            if (bitmap != null)
            {
                mBuiltInGraphicsHost.EndRender();

                Dispatcher.BeginInvoke(() =>
                {
                    bitmap.Invalidate();
                });
            }
        }

        public void DisposeDevice()
        {
            SizeChanged -= SoftwareRenderer_SizeChanged;
            wBitmap = null;
        }

        /// <summary>
        /// Get the graphics display if available.
        /// </summary>
        public IGraphicsDisplay GraphicsDisplay { get; private set; }

        /// <summary>
        /// Calls wqhen this instance has been created.
        /// </summary>
        /// <param name="graphicsDisplay"></param>
        public void Initialize(IGraphicsDisplay graphicsDisplay)
        {
            GraphicsDisplay = graphicsDisplay;
        }

        public void DrawTriangles(FastStructList<Vector2> vertices, FastStructList<ushort> indices, FastStructList<Datatypes.Color> colors)
        {

        }

        public System.Collections.ObjectModel.ReadOnlyCollection<Datatypes.Texture> Textures
        {
            get { return mBuiltInGraphicsHost.Textures; }
        }

        public bool Ready
        {
            get { return true; }
        }

        public bool ManualRendering
        {
            get
            {
                return false;
            }
        }
    }
}
